﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using EzyGoEzySystem.Interfaces;
using EzyGoEzySystem.Models;
using EzyGoEzySystem.Services.DataStructures;

namespace EzyGoEzySystem.Services
{
    public class TrafficTracking : ITrafficTracking 
    {
        public Graph BusStopGraph { get; set; }
        public TrafficTracking(EzyGoEzyDBContext dbContext)
        {
            BusStopGraph = new Graph(dbContext);
        }

        public List<Node> SuggestRoute(string startBusStop, string endBusStop)
        {
            SortedSet<BusStopRecord> pQueue = new SortedSet<BusStopRecord>();
            SortedSet<BusStopRecord> visited = new SortedSet<BusStopRecord>();

            Node startNode = BusStopGraph.FindNode(startBusStop);
            Node endNode = BusStopGraph.FindNode(endBusStop);

            BusStopRecord busStopRecord = new BusStopRecord();
            busStopRecord.Node = startNode;
            busStopRecord.H = Math.Abs(startNode.H - endNode.H);
            busStopRecord.F = busStopRecord.H;
            pQueue.Add(busStopRecord);

            BusStopRecord current = null;
            while (pQueue.Count != 0)
            {
                current = pQueue.ElementAt(0);
                pQueue.Remove(current);
                visited.Add(current);

                if (current.Node == endNode)
                {
                    break;
                }

                List<Connection> connections = current.Node.Connection;
                foreach (var connection in connections)
                {
                    Node toNode = BusStopGraph.BusStop[connection.ToBusStop];
                    double endNodeCost = current.G + connection.Cost;
                    if (ContainNode(visited, toNode) != null)
                        continue;

                    BusStopRecord endNodeRecord = ContainNode(pQueue, toNode);
                    if (endNodeRecord != null)
                    {
                        if (endNodeCost >= endNodeRecord.G)
                            continue;

                        pQueue.Remove(endNodeRecord);
                        endNodeRecord.G = endNodeCost;
                        endNodeRecord.F = endNodeRecord.G + endNodeRecord.H;
                        endNodeRecord.Parent = current;
                        pQueue.Add(endNodeRecord);
                    }
                    else
                    {
                        endNodeRecord = new BusStopRecord();
                        endNodeRecord.G = endNodeCost;
                        endNodeRecord.H = Math.Abs(endNodeRecord.H - endNode.H);
                        endNodeRecord.F = endNodeRecord.G + endNodeRecord.H;
                        endNodeRecord.Node = toNode;
                        endNodeRecord.Parent = current;
                        pQueue.Add(endNodeRecord);
                    }

                }
            }

            if (current.Node != endNode) return null;

            List<Node> path = new List<Node>();
            while (current.Parent != null)
            {
                path.Add(current.Node);
                current = current.Parent;
            }
            path.Add(current.Node);
            
            return path;
        }

        public BusStopRecord ContainNode(SortedSet<BusStopRecord> records, Node node)
        {
            foreach (var record in records)
            {
                if (record.Node == node)
                    return record;
            }
            return null;
        }
      
        //this method will find the bus go through two bus stop
        public List<string> bus(string startStop, string endStop, EzyGoEzyDBContext dbContext)
        {
            List<string> buses = new List<string>();
            int idStartStop = BusStopGraph.IDBusStop(startStop, dbContext);
            int idEndStop = BusStopGraph.IDBusStop(endStop, dbContext);
            var entry = from routeEntry in dbContext.Route
                        where routeEntry.StartBusStopID == idStartStop &&
                              routeEntry.EndBusStopID == idEndStop
                        select routeEntry;
            var routes = entry.ToList<Route>();
            foreach (var route in routes)
            {
                int idBus = route.BusID;
                buses.Add(BusStopGraph.NameBus(idBus, dbContext));
            }
            return buses;
        }
    }

    public class BusStopRecord : IComparable
    {
        public double F { get; set; }
        public double H { get; set; }
        public double G { get; set; }
        public BusStopRecord Parent { get; set; }
        public Node Node { get; set; }

        public BusStopRecord()
        {
            F = 0;
            H = 0;
            G = 0;
        }



        public int CompareTo(object obj)
        {
            BusStopRecord obj1 = (BusStopRecord)obj;
            if (this.F == obj1.F)
                return 0;
            else if (this.F < obj1.F)
                return -1;
            else return 1;
        }

       
    }

}